import { effect, createProxy } from "./index.js";
/**
 *  4.9 watch 实现原理
 * @param {*} source
 * @param {*} cb
 * @param {*} options
 */
function watch(source, cb, options = {}) {
  let getter;
  if (typeof source === "function") {
    getter = source;
  } else {
    getter = () => traverse(source);
  }
  //   定义旧值和新值
  let oldValue, newValue;
  // cleanup 用来存储用户注册的过期回调
  let cleanup;
  //定义onCleanup函数
  function onCleanup(fn) {
    // 将过期回调函数存储到 cleanup 中
    cleanup = fn;
  }
  const job = () => {
    newValue = effectFn();
    // 在调用回调函数cb之前,先调用过期回调
    if (cleanup) {
      cleanup();
    }
    // 将onCleanup函数传递给回调函数cb,以方便用户使用
    cb(newValue, oldValue, onCleanup);
    oldValue = newValue;
  };
  // 使用effect注册副作用函数时,开启lazy选项,并把返回值存储到effectFn中,以便后续手动调用
  const effectFn = effect(() => getter(), {
    lazy: true,
    scheduler: () => {
      if (options.flush === "post") {
        const p = Promise.resolve();
        p.then(job);
      } else {
        job();
      }
    },
  });

  if (options.immediate) {
    job();
  } else {
    oldValue = effectFn();
  }
}

function traverse(value, seen = new Set()) {
  // 如果要读取的数据是原始值,或者已经被读取过了,那么什么都不做
  if (typeof value !== "object" || value === null || seen.has(value)) return;
  // 将数据添加到seen中,代表遍历地读取过了,避免循环引用的死循环
  seen.add(value);
  // 暂时不考虑数组等其他结构
  // 假设value是一个对象,遍历对象的每个属性,并递归调用traverse进行处理
  for (const key in value) {
    traverse(value[key], seen);
  }
  return value;
}

//测试用例
const data = { foo: 1 };
const obj = createProxy(data);
let finalData;
watch(obj, async (newValue, oldValue, onCleanup) => {
  let expired = false;
  //  解决静态问题
  onCleanup(() => {
    expired = true;
  });
  const res = await new Promise((resolve) => {
    setTimeout(() => {
      resolve("done");
    }, 1000);
  });
  if (!expired) {
    finalData = res;
    console.log("watch:", newValue, oldValue, finalData);
  }
});
// 第一次修改
obj.foo++;
setTimeout(() => {
  // 200ms后第二次修改
  obj.foo++;
}, 200);
